package com.bida.service.impl;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bida.constant.MessageConstant;
import com.bida.constant.WebsocketConstant;
import com.bida.context.BaseContext;
import com.bida.domain.dto.*;
import com.bida.domain.po.*;
import com.bida.domain.vo.OrderPaymentVO;
import com.bida.domain.vo.OrderStatisticsVO;
import com.bida.domain.vo.OrderSubmitVO;
import com.bida.domain.vo.OrderVO;
import com.bida.exception.AddressBookBusinessException;
import com.bida.exception.BaseException;
import com.bida.exception.OrderBusinessException;
import com.bida.exception.ShoppingCartBusinessException;
import com.bida.mapper.OrdersMapper;
import com.bida.result.PageResult;
import com.bida.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bida.utils.HttpClientUtil;
import com.bida.utils.WeChatPayUtil;
import com.bida.websocket.WebSocketServer;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.bida.constant.MessageConstant.*;
import static com.bida.constant.OrdersConstant.*;
import static com.bida.constant.WebsocketConstant.NEW_ORDERS_NOTICE;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author ly
 * @since 2024-09-08
 */
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {

    @Autowired
    private IOrderDetailService orderDetailService;

    @Autowired
    private IShoppingCartService shoppingCartService;

    @Autowired
    private IAddressBookService addressBookService;

    @Autowired
    private WeChatPayUtil weChatPayUtil;

    @Autowired
    private IUserService userService;

    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 用户下单
     * @param ordersSubmitDTO
     * @return
     */
    @Override
    @Transactional
    public OrderSubmitVO userSubmitOrder(OrdersSubmitDTO ordersSubmitDTO) {
        OrderSubmitVO orderSubmitVO = new OrderSubmitVO();
        Long addressBookId = ordersSubmitDTO.getAddressBookId();

        int payMethod = ordersSubmitDTO.getPayMethod();
        String remark = ordersSubmitDTO.getRemark();
        LocalDateTime estimatedDeliveryTime = ordersSubmitDTO.getEstimatedDeliveryTime();
        Integer deliveryStatus = ordersSubmitDTO.getDeliveryStatus();
        Integer tablewareNumber = ordersSubmitDTO.getTablewareNumber();
        Integer tablewareStatus = ordersSubmitDTO.getTablewareStatus();
        Integer packAmount = ordersSubmitDTO.getPackAmount();
        BigDecimal amount = ordersSubmitDTO.getAmount();
        // 1.获取当前登录用户id
        Long userId = BaseContext.getCurrentId();
        AddressBook addressBook = addressBookService.getById(addressBookId);
        if (addressBook == null){
            throw new AddressBookBusinessException(ADDRESS_BOOK_IS_NULL);
        }

        // 检查用户的收货地址是否超出配送范围 (string: 省名 + 市名 + 区名 + 详细地址)
        // todo 去注册一个合法的域名，在打开注释
        // 好像微信小程序需要去注册一个合法的域名，内网穿透得到的域名好像不行
//        checkOutOfRange(addressBook.getProvinceName()+addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail());

        // 2.获取用户当前购物车信息
        List<ShoppingCart> shoppingCartList =  shoppingCartService.lambdaQuery()
                .eq(ShoppingCart::getUserId, userId)
                .list();
        if (shoppingCartList == null || shoppingCartList.isEmpty()){
            throw new ShoppingCartBusinessException(SHOPPING_CART_IS_NULL);
        }
        // 3.获取地址信息
        Orders orders = new Orders();
        orders.setNumber(String.valueOf(System.currentTimeMillis()));
        orders.setUserId(userId);
        orders.setAddressBookId(addressBookId);
        orders.setOrderTime(LocalDateTime.now());
        orders.setPayMethod(payMethod);
        orders.setAmount(amount);
        orders.setRemark(remark);
        orders.setPhone(addressBook.getPhone());
        orders.setAddress(addressBook.getDetail());
        orders.setConsignee(addressBook.getConsignee());
        orders.setEstimatedDeliveryTime(estimatedDeliveryTime);
        orders.setDeliveryStatus(deliveryStatus);
        orders.setPackAmount(packAmount);
        orders.setTablewareNumber(tablewareNumber);
        orders.setTablewareStatus(tablewareStatus);
        // 4.保存订单信息
        this.save(orders);
        // 5.订单明细信息
        for (ShoppingCart shoppingCart : shoppingCartList) {
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setName(shoppingCart.getName());
            orderDetail.setImage(shoppingCart.getImage());
            orderDetail.setOrderId(orders.getId());
            orderDetail.setDishId(shoppingCart.getDishId());
            orderDetail.setSetmealId(shoppingCart.getSetmealId());
            orderDetail.setDishFlavor(shoppingCart.getDishFlavor());
            orderDetail.setNumber(shoppingCart.getNumber());
            orderDetail.setAmount(shoppingCart.getAmount());
            // 保存
            orderDetailService.save(orderDetail);
        }
        // 6.清空购物车数据
        QueryWrapper<ShoppingCart> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        shoppingCartService.remove(queryWrapper);

        // 7.封装数据返回
        orderSubmitVO.setId(orders.getId());
        orderSubmitVO.setOrderNumber(orders.getNumber());
        orderSubmitVO.setOrderAmount(orders.getAmount());
        orderSubmitVO.setOrderTime(orders.getOrderTime());
        return orderSubmitVO;
    }


    /**
     * 获取商铺配置信息
     */
    @Value("${bida.shop.address}")
    private String shopAddress;
    @Value("${bida.baidu.ak}")
    private String ak;
    /**
     * 检查客户的收货地址是否超出配送范围
     * @param address
     * （(address: 省名 + 市名 + 区名 + 详细地址)）
     */
    private void checkOutOfRange(String address) {
        // 1、获取经纬度
        // 文档地址：https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-base
        // 1.1、获取店铺经纬度
        String BaseUrl = "https://api.map.baidu.com/geocoding/v3";
        Map map = new HashMap<>();
        map.put("address", shopAddress);
        map.put("output", "json");
        map.put("ak",ak);
        String shopCoordinate = HttpClientUtil.doGet(BaseUrl, map);
        JSONObject jsonObject = JSON.parseObject(shopCoordinate);

        log.error("请求回调:"+shopCoordinate);

        // 1.2、判断请求是否失败
        if(!jsonObject.getString("status").equals("0")){
            throw new OrderBusinessException(MessageConstant.GET_ADDRESS_NO_SUCCESS);
        }

        // 1.3、成功，解析数据
        JSONObject location = jsonObject.getJSONObject("result").getJSONObject("location");
        // 纬度值
        String lat = location.getString("lat");
        // 经度值
        String lng = location.getString("lng");

        // 1.4、拼接店铺经纬度坐标
        String shopLngLat = lat + "," + lng;

        // 2、获取用户收获地址经纬度坐标
        // 2.1、拼接请求参数
        map.put("address",address);
        String userCoordinate = HttpClientUtil.doGet(BaseUrl, map);
        // 2.2、判断请求是否失败
        jsonObject = JSON.parseObject(userCoordinate);
        if(!jsonObject.getString("status").equals("0")){
            throw new OrderBusinessException(MessageConstant.GET_USER_ADDRESS_NO_SUCCESS);
        }
        //数据解析
        location = jsonObject.getJSONObject("result").getJSONObject("location");
        lat = location.getString("lat");
        lng = location.getString("lng");
        //用户收货地址经纬度坐标
        String userLngLat = lat + "," + lng;

        // 3、配送路线规划
        // 文档地址：https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-lwrouteplanapi/dirve
        String BaseUrl2 = "https://api.map.baidu.com/directionlite/v1/driving";
        map.put("origin",shopLngLat);
        map.put("destination",userLngLat);
        map.put("steps_info","0");
        //路线规划
        String json = HttpClientUtil.doGet(BaseUrl2, map);
        jsonObject = JSON.parseObject(json);
        if(!jsonObject.getString("status").equals("0")){
            throw new OrderBusinessException(MessageConstant.GET_GOOD_WAY_FAILURE);
        }
        //数据解析
        JSONObject result = jsonObject.getJSONObject("result");
        JSONArray jsonArray = (JSONArray) result.get("routes");
        Integer distance = (Integer) ((JSONObject) jsonArray.get(0)).get("distance");

        if(distance > 5000){
            //配送距离超过5000米
            throw new OrderBusinessException(MessageConstant.TOO_LONG_ADDRESS);
        }

    }

    /**
     * 订单支付
     *
     * @param ordersPaymentDTO
     * @return
     */
    @Override
    public OrderPaymentVO paymentOrder(OrdersPaymentDTO ordersPaymentDTO) throws Exception {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();
        User user = userService.getById(userId);

        // 获取订单应支付金额
        BigDecimal amount = this.lambdaQuery()
                .eq(Orders::getNumber, ordersPaymentDTO.getOrderNumber())
                .eq(Orders::getUserId, userId)
                .one()
                .getAmount();

        //调用微信支付接口，生成预支付交易单
        JSONObject jsonObject = weChatPayUtil.pay(
                //商户订单号
                ordersPaymentDTO.getOrderNumber(),
                //支付金额，单位 元
                amount,
                //商品描述
                "送必达订单",
                //微信用户的openid
                user.getOpenid()
        );

        if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
            throw new OrderBusinessException("该订单已支付");
        }

        OrderPaymentVO vo = jsonObject.toJavaObject(OrderPaymentVO.class);
        vo.setPackageStr(jsonObject.getString("package"));

        return vo;
    }

    /**
     * 支付成功，修改订单状态
     *
     * @param outTradeNo
     */
    @Override
    public void paySuccess(String outTradeNo) {
        // 当前登录用户id
        Long userId = BaseContext.getCurrentId();
        // 根据订单号查询当前用户的订单
        Orders ordersDB = this.lambdaQuery()
                .eq(Orders::getNumber, outTradeNo)
                .eq(Orders::getUserId, userId)
                .one();
        // 根据订单id更新订单的状态、支付方式、支付状态、结账时间
        Orders orders = new Orders();
        orders.setId(ordersDB.getId());
        orders.setStatus(TO_BE_CONFIRMED);
        orders.setPayStatus(PAID);
        orders.setCheckoutTime(LocalDateTime.now());
        this.updateById(orders);

        // 通过Websocket向浏览器推送消息 type orderId content
        Map map = new HashMap<>();
        map.put(WebsocketConstant.TYPE,WebsocketConstant.NEW_ORDERS_NOTICE);
        map.put(WebsocketConstant.ORDER_ID,ordersDB.getId());
        map.put(WebsocketConstant.CONTENT,WebsocketConstant.ORDER_NO + outTradeNo);

        String json = JSON.toJSONString(map);
        webSocketServer.sendToAllClient(json);

    }

    /**
     * 历史订单查询
     * @param page
     * @param pageSize
     * @param status
     * @return
     */
    @Override
    public PageResult getHistoryOrders(int page, int pageSize, Integer status) {
        // 1.获取当前登录用户id
        Long userId = BaseContext.getCurrentId();
        IPage<Orders> nowPage = new Page<>(page, pageSize);
        List<OrderVO> orderVOList = new ArrayList();

        QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();

        queryWrapper.eq("user_id", userId);
        if (status != null){
            queryWrapper.eq("status", status);
        }
        queryWrapper.orderByDesc("order_time");
        IPage<Orders> ordersIPage = this.page(nowPage, queryWrapper);

        if (ordersIPage != null && ordersIPage.getTotal() > 0){
            for (Orders orders : ordersIPage.getRecords()) {

                Long ordersId = orders.getId();
                List<OrderDetail> orderDetailList = orderDetailService.lambdaQuery()
                        .eq(OrderDetail::getOrderId, ordersId)
                        .list();
                OrderVO orderVO = new OrderVO();
                BeanUtils.copyProperties(orders, orderVO);
                orderVO.setOrderDetailList(orderDetailList);
                orderVOList.add(orderVO);
            }
        }
        return new PageResult(nowPage.getTotal(), orderVOList);
    }

    /**
     * 查询订单详情
     * @param id
     * @return
     */
    @Override
    public OrderVO getOrdersInfoById(Long id) {
        // 获取当前登录用户信息
        OrderVO orderVO = new OrderVO();

        // 查询
        Orders orders = this.getById(id);

        List<OrderDetail> orderDetailList = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, id)
                .list();

        // 封装数据
        BeanUtils.copyProperties(orders, orderVO);
        orderVO.setOrderDetailList(orderDetailList);
        return orderVO;
    }

    /**
     * 取消订单
     * @param id
     */
    @Override
    public void cancelOrderById(Long id) throws Exception {
        // 根据id查询订单
        Orders ordersDB = this.getById(id);

        // 校验订单是否存在
        if (ordersDB == null) {
            throw new OrderBusinessException(MessageConstant.ORDER_HAS_GET);
        }

        // 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消
        // 未接单状态下允许直接退单
        if (ordersDB.getStatus() > TO_BE_CONFIRMED) {
            throw new OrderBusinessException(ORDER_STATUS_ERROR);
        }

        Orders orders = new Orders();
        orders.setId(ordersDB.getId());

        // 订单处于待接单状态下取消，需要进行退款
        if (ordersDB.getStatus().equals(TO_BE_CONFIRMED)) {
            //调用微信支付退款接口
            weChatPayUtil.refund(
                    //商户订单号
                    ordersDB.getNumber(),
                    //商户退款单号
                    ordersDB.getNumber(),
                    //退款金额，单位 元
                    ordersDB.getAmount(),
                    //原订单金额
                    ordersDB.getAmount());

            //支付状态修改为 退款
            orders.setPayStatus(REFUND);
        }
        // 更新订单状态、取消原因、取消时间
        orders.setStatus(CANCELLED);
        orders.setCancelReason("用户取消");
        orders.setCancelTime(LocalDateTime.now());
        this.updateById(orders);
    }

    /**
     * 再来一单
     * @param id
     */
    @Override
    public void repetitionOrderAgain(Long id) {
        // 查询当前用户id
        Long userId = BaseContext.getCurrentId();
        // 获取订单详情信息
        List<OrderDetail> orderDetailList = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, id)
                .list();

        // 将订单详情对象转换为购物车对象
        List<ShoppingCart> shoppingCartList = orderDetailList.stream().map(x -> {
            ShoppingCart shoppingCart = new ShoppingCart();
//            shoppingCart.setName(x.getName());
//            shoppingCart.setImage(x.getImage());
//            shoppingCart.setDishId(x.getDishId());
//            shoppingCart.setSetmealId(x.getSetmealId());
//            shoppingCart.setDishFlavor(x.getDishFlavor());
//            shoppingCart.setNumber(x.getNumber());
//            shoppingCart.setAmount(x.getAmount());
            BeanUtils.copyProperties(x, shoppingCart);

            shoppingCart.setUserId(userId);
            shoppingCart.setCreateTime(LocalDateTime.now());
            return shoppingCart;
        }).collect(Collectors.toList());

        // 将购物车对象批量添加到数据库
        shoppingCartService.saveBatch(shoppingCartList);
    }

    /**
     * 订单搜索
     * @param ordersPageQueryDTO
     * @return
     */
    @Override
    public PageResult getOrdersPageConditionSearch(OrdersPageQueryDTO ordersPageQueryDTO) {
        int page = ordersPageQueryDTO.getPage();
        int pageSize = ordersPageQueryDTO.getPageSize();
        String number = ordersPageQueryDTO.getNumber();
        String phone = ordersPageQueryDTO.getPhone();
        Integer status = ordersPageQueryDTO.getStatus();
        LocalDateTime beginTime = ordersPageQueryDTO.getBeginTime();
        LocalDateTime endTime = ordersPageQueryDTO.getEndTime();

        List<OrderVO> orderVOList = new ArrayList<>();

        IPage<Orders> nowPage =  new Page<>(page,pageSize);
        QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
        // 条件过滤
        if (!StringUtils.isEmpty(number)){
            queryWrapper.eq("number", number);
        }
        if (!StringUtils.isEmpty(phone)){
            queryWrapper.eq("phone", phone);
        }
        if (status != null){
            queryWrapper.eq("status", status);
        }
        if (beginTime != null){
            queryWrapper.gt("order_time", beginTime);
        }
        if (endTime != null){
            queryWrapper.lt("order_time", endTime);
        }
        // 查询
        IPage<Orders> ordersIPage = this.page(nowPage, queryWrapper);
        // 处理
        for (Orders orders : ordersIPage.getRecords()) {
            Long id = orders.getId();

            List<OrderDetail> orderDetailList = orderDetailService.lambdaQuery()
                    .eq(OrderDetail::getOrderId, id)
                    .list();
            OrderVO orderVO = new OrderVO();
            BeanUtils.copyProperties(orders, orderVO);
            orderVO.setOrderDetailList(orderDetailList);
            orderVOList.add(orderVO);
        }
        return new PageResult(ordersIPage.getTotal(), orderVOList);
    }

    /**
     * 各个状态的订单数量统计
     * @return
     */
    @Override
    public OrderStatisticsVO getAllOrderStatistics() {
        // 使用并发
        // 待接单数量
        CompletableFuture<Integer> toBeConfirmedFuture = CompletableFuture.supplyAsync(() -> {
            Integer count = this.lambdaQuery()
                    .eq(Orders::getStatus, TO_BE_CONFIRMED)
                    .count();
            return count;
        });
        //待派送数量
        CompletableFuture<Integer> confirmedFuture = CompletableFuture.supplyAsync(() -> {
            Integer count = this.lambdaQuery()
                    .eq(Orders::getStatus, CONFIRMED)
                    .count();
            return count;
        });
        // 派送中数量
        CompletableFuture<Integer> deliveryInProgressFuture =  CompletableFuture.supplyAsync(() -> {
            Integer count = this.lambdaQuery()
                    .eq(Orders::getStatus, DELIVERY_IN_PROGRESS)
                    .count();
            return count;
        });
        // 阻塞，保证线程完全执行完
        CompletableFuture.allOf(toBeConfirmedFuture, deliveryInProgressFuture, confirmedFuture).join();
        try {
            OrderStatisticsVO orderStatisticsVO = new OrderStatisticsVO();
            orderStatisticsVO.setToBeConfirmed(toBeConfirmedFuture.get());
            orderStatisticsVO.setConfirmed(confirmedFuture.get());
            orderStatisticsVO.setDeliveryInProgress(deliveryInProgressFuture.get());
            return orderStatisticsVO;
        }catch (Exception e){
            log.error("获取订单各状态数量信息失败");
            throw new BaseException("获取订单各状态数量信息失败");
        }
    }

    /**
     * 接单
     * @param id
     */
    @Override
    public void confirmOrdersById(Long id) {
        this.lambdaUpdate()
                .set(Orders::getStatus, CONFIRMED)
                .eq(Orders::getId, id)
                .update();
    }

    /**
     * 拒单
     * @param ordersRejectionDTO
     */
    @Override
    public void rejectionOrdersByIdReason(OrdersRejectionDTO ordersRejectionDTO) throws Exception {
        Long id = ordersRejectionDTO.getId();
        String rejectionReason = ordersRejectionDTO.getRejectionReason();
        // 根据id查询订单
        Orders orders = this.getById(id);
        // 只有在待接单状态下才可以拒单
        if (orders == null || !orders.getStatus().equals(TO_BE_CONFIRMED)){
            throw new OrderBusinessException(ORDER_STATUS_ERROR);
        }
        // 支付状态
        Integer payStatus = orders.getPayStatus();
        if (payStatus.equals(PAID)){
            // 如果已支付，需要退款
            String refund = weChatPayUtil.refund(
                    orders.getNumber(),
                    orders.getNumber(),
                    orders.getAmount(),
                    orders.getAmount()
            );
            log.info("申请退款：{}", refund);
        }
        // 拒单需要退款，根据订单id更新订单状态、拒单原因、取消时间
        Orders nowOrders = new Orders();
        nowOrders.setId(orders.getId());
        orders.setStatus(CANCELLED);
        orders.setRejectionReason(rejectionReason);
        orders.setCancelTime(LocalDateTime.now());
        this.updateById(nowOrders);

    }

    /**
     * 取消订单
     * @param orderCancelDTO
     */
    @SneakyThrows
    @Override
    public void cancelOrderByIdReason(OrdersCancelDTO orderCancelDTO) {
     Long id = orderCancelDTO.getId();
     String cancelReason = orderCancelDTO.getCancelReason();
     this.cancelOrderById(id);
     this.lambdaUpdate()
             .set(Orders::getCancelReason, cancelReason)
             .eq(Orders::getId, id)
             .update();
    }

    /**
     * 派送订单
     * @param id
     */
    @Override
    public void deliveryOrderById(Long id) {
        Orders orders = this.getById(id);
        if (orders == null || !orders.getStatus().equals(CONFIRMED)){
            throw new OrderBusinessException(ORDER_STATUS_ERROR);
        }
        this.lambdaUpdate()
                .set(Orders::getStatus, DELIVERY_IN_PROGRESS)
                .eq(Orders::getId, id)
                .update();
    }

    /**
     * 完成订单
     * @param id
     */
    @Override
    public void completeOrderById(Long id) {
        Orders orders = this.getById(id);
        if (orders == null || !orders.getStatus().equals(DELIVERY_IN_PROGRESS)){
            throw new OrderBusinessException(ORDER_STATUS_ERROR);
        }
        this.lambdaUpdate()
                .set(Orders::getStatus, COMPLETED)
                .eq(Orders::getId, id)
                .update();
    }

    /**
     * 用户催单
     * @param id
     */
    @Override
    public void userReminder(Long id) {
        // 查询订单
        Orders orders = this.getById(id);
        if (orders == null){
            throw new OrderBusinessException(ORDER_NOT_FOUND);
        }
        // 用户催单
        Map map = new HashMap();
        map.put(WebsocketConstant.TYPE, WebsocketConstant.USER_REMINDER_ORDERS);
        map.put(WebsocketConstant.ORDER_ID, id);
        map.put(WebsocketConstant.CONTENT,WebsocketConstant.ORDER_NO + orders.getNumber());
        // 通过Websocket向客户端浏览器推送消息
        webSocketServer.sendToAllClient(JSON.toJSONString(map));
    }

}
